#include "GLCallBatcher.h"
#include "FTBitmapChar.h"
#include <string.h>
#include <assert.h>
const int dataPerVextex = 5;

/**
 * Constructor.
 * @param quads Size of buffer in quads.
 * @param increase Increase step on reallocation due to too small buffer.
 */
GLCallBatcher::GLCallBatcher(uint quads, uint increase) {
	numQuads        = 0;
	vertexData      = NULL;
	indices         = NULL;
	textureID       = 0;
	sizeCache       = 0;
	drawCallCount   = 0;
    this->increase  = increase;
	Reallocate(quads);
}

GLCallBatcher::~GLCallBatcher() {
	release();
	numQuads        = 0;
	vertexData      = NULL;
	indices         = NULL;
	textureID       = 0;
	sizeCache       = 0;
	drawCallCount   = 0;
}

// deletes memory for arrays
void GLCallBatcher::release() {
	if (vertexData != NULL)	{
		delete[] vertexData;
		vertexData = NULL;
	}
	if (indices != NULL) {
		delete[] indices;
		indices = NULL;
	}
	sizeCache = 0;
}

// adds a quad of a give colour, alpha, etc
void GLCallBatcher::addQuad(GLuint textureID, int color, float alpha, bool transparent, bool smooth, const float* pTexCoords, const float* pVertices) {
	setAttribs(textureID, color, alpha, transparent, smooth);
	addQuad(pTexCoords, pVertices);
}	// Render

// adds a quad and assumes that the colour, alpha is same as previous
void GLCallBatcher::addQuad(const float* pTexCoords, const float* pVertices) {
	if (numQuads >= sizeCache) {
		Reallocate(increase);
	}
	
	int currIndex = numQuads*verticesPerQuad*dataPerVextex;
	for (int n = 0; n < verticesPerQuad; n++) {
		// x,y,z: note: do not need to copy 3rd coordinate, just zero it
		vertexData[currIndex+n*dataPerVextex] = pVertices[n*compVertPos];
		vertexData[currIndex+n*dataPerVextex+1] = pVertices[n*compVertPos+1];
		// u,v
		vertexData[currIndex+n*dataPerVextex+3] = pTexCoords[n*compVertTex];
		vertexData[currIndex+n*dataPerVextex+4] = pTexCoords[n*compVertTex+1];
	}
	numQuads++;
}	// Render

// sets the current colour, alpha, etc,
// if this is different than previous then the current quads have to be rendered
// before adding any new ones
void GLCallBatcher::setAttribs(GLuint textureID, int color, float alpha, bool transparent, bool smooth) {
	if (alpha > 1.0f) alpha = 1.0f;
	if (this->textureID != textureID || this->color != color || this->alpha != alpha || this->transparent != transparent || this->smooth != smooth)	{
		renderCurr();
		this->textureID = textureID;
		this->color = color;
		this->alpha = alpha;
		this->transparent = transparent;
		this->smooth = smooth;
	}
}	// Render

/*
 * Render the currently stored quads.
 */
void GLCallBatcher::renderCurr() {
	if (numQuads == 0)
        return;

	if (transparent || alpha < 0.99f) {
		glEnable(GL_BLEND);	
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
	else 
		glDisable(GL_BLEND);
    
	glBindTexture(GL_TEXTURE_2D, textureID);
    
	if (smooth) {
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	} else {
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}
    
	glVertexPointer(3, GL_FLOAT, sizeof(float) * dataPerVextex, vertexData);
	glTexCoordPointer(compVertTex, GL_FLOAT, sizeof(float) * dataPerVextex, &vertexData[3]);
	assert(numQuads * indicesPerQuad < 0xffff);
#ifdef DEBUG_FREETYPE
    glDisable(GL_BLEND);
    glDrawElements(GL_TRIANGLES, (GLsizei)(numQuads*indicesPerQuad), GL_UNSIGNED_SHORT, indices);
    glEnable(GL_BLEND);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glColor4f(1.0f, 0.2f, 0.2f, 1.0f);
#endif
    glDrawElements(GL_TRIANGLES, (GLsizei)(numQuads*indicesPerQuad), GL_UNSIGNED_SHORT, indices);
    
	quadCount     += numQuads;
	drawCallCount += 1;
	numQuads       = 0;
    
    // Restore old states.
    glEnable(GL_BLEND);
}

// increase the memory alloacation
// new memory is allocated and old memory is copied, then old memory is deleted
void GLCallBatcher::Reallocate(int increase) {
	int newSize = numQuads+increase;
	float* pVertices = new float[newSize*verticesPerQuad*dataPerVextex];	// x,y,z
	memset(pVertices, 0, newSize*verticesPerQuad*dataPerVextex*sizeof(float));
	GLushort* pIndices = new GLushort[newSize*indicesPerQuad];   // 2 triangles per quad
	// indices never change
	for (int n = 0; n < newSize; n++) {
		pIndices[n*indicesPerQuad] = (GLshort)n*verticesPerQuad;
		pIndices[n*indicesPerQuad+1] = (GLshort)n*verticesPerQuad+1;
		pIndices[n*indicesPerQuad+2] = (GLshort)n*verticesPerQuad+2;
		pIndices[n*indicesPerQuad+3] = (GLshort)n*verticesPerQuad+1;
		pIndices[n*indicesPerQuad+4] = (GLshort)n*verticesPerQuad+3;
		pIndices[n*indicesPerQuad+5] = (GLshort)n*verticesPerQuad+2;
	}
	if (numQuads > 0) {
		int numVert = numQuads*verticesPerQuad;
		memcpy(pVertices, vertexData, numVert*dataPerVextex*sizeof(float));
//		memcpy(pTexCoords, m_pTexCoords, numVert*compVertTex*sizeof(float));
		release();
	}
	vertexData = pVertices;
//	m_pTexCoords = pTexCoords;
	indices = pIndices;
	sizeCache = newSize;
}
